using System;
using System.Xml;
using System.Web;
using System.Collections;
using System.Globalization;
using System.Collections.Generic;
using System.Collections.Specialized;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

using RealDolmen.Helpers;
using RealDolmen.SharePoint.Helpers;
using RealDolmen.SharePoint.ListFeatures.Elements;

namespace RealDolmen.SharePoint.ListFeatures
{
    public delegate void InitListFeature(ListFeature listFeature);

    public class ListFeature
    {
        public enum Keys
        {
            Title
        }



        #region Variables
        internal ListFeatureKeyManager KeyManager;
        internal ListFeatureTracker Tracker;

        private SPList parentList;
        private ListFeatureDefinition definition;
        private Guid id;
        #endregion // Variables

        #region Properties
        public Guid Id
        {
            get { return id; }
            protected set { id = value; }
        }

        public SPList ParentList
        {
            get { return parentList; }
            protected set { parentList = value; }
        }

        public ListFeatureDefinition Definition
        {
            get { return definition; }
            protected set { definition = value; }
        }

        public string TrackerData
        {
            get { return Tracker.Data; }
        }

        public string SettingsUrl
        {
            get
            {
                string url = Definition.SettingsUrl;

                if (url.IndexOf('?') < 0)
                    url = string.Format("{0}?List={1}&Feature={2}", url, ParentList.ID, Id);

                return url;
            }
        }

        public string SettingsUrlRelative
        {
            get
            {
                return string.Format("{0}/{1}", ParentList.ParentWeb.Url, SettingsUrl);
            }
        }

        public string SettingsUrlAbsolute
        {
            get
            {
                return SPUtility.GetFullUrl(ParentList.ParentWeb.Site, SettingsUrlRelative);
            }
        }
        #endregion // Properties


        #region Title
        public string GetTitle(CultureInfo ci)
        {
            string title = GetPropertyValue("Title");
            if (string.IsNullOrEmpty(title))
            {
                title = Definition.GetTitle(ci);
            }
            return title;
        }

        public void SetTitle(string title)
        {
            if (title != null)
            {
                if (title == Definition.FeatureDefinition.GetTitle(SPHelper.CurrentCultureInfo))
                    DeletePropertyValue("Title");
                else
                    SetPropertyValue("Title", title);
            }
        }
        public bool HasCustomTitle
        {
            get { return ! string.IsNullOrEmpty(GetPropertyValue("Title"));  }
        }
        #endregion // Title

        #region Settings
        private ListFeatureSettings settings;
        public ListFeatureSettings Settings
        {
            get
            {
                if (settings == null)
                    settings = ListFeatureSettings.Create(this);
                return settings;
            }
        }
        #endregion // Settings

        #region Elements
        protected ListFeatureElements ConstructElementDefinitionCollection()
        {
            ListFeatureElements elements = new ListFeatureElements(this.Definition);

            foreach (ListFeatureElementsFile file in Definition.ElementFileList)
            {
                foreach (XmlNode node in file.GetElementNodes())
                {
                    if (node.NodeType == XmlNodeType.Element)
                        elements.AddFromFeature(node, file, this);
                }
            }
            return elements;
        }
        private ListFeatureElements listFeatureElements;
        public ListFeatureElements Elements
        {
            get
            {

                if (listFeatureElements == null)
                    listFeatureElements = ConstructElementDefinitionCollection();
                return listFeatureElements;
            }
        }

        internal void UpdateTrackerDataReferences(string oldData)
        {
            List<ListFeatureElement> elements = Elements.Get();

            foreach (ListFeatureElement element in elements)
            {
                if (element is IListFeatureTrackerReferencee)
                {
                    IListFeatureTrackerReferencee reference = element as IListFeatureTrackerReferencee;
                    reference.UpdateTrackerReference(this, oldData);
                }
            }
        }

        #endregion // Elements 


        #region Instantiating
        private bool Instantiate(SPList list, Guid featureGuid)
        {
            SPWeb web = list.ParentWeb;
            bool returnValue = Instantiate(list, featureGuid, web);
            SPHelper.TryDisposeWeb(web);

            return returnValue;
        }

        private bool Instantiate(SPList list, Guid featureGuid, SPWeb web)
        {
            ParentList = list;
            SetGuid(featureGuid);

            string typeGuid = GetPropertyValue("Definition");

            if (string.IsNullOrEmpty(typeGuid))
                return false;
            else
            {
                Definition = ListFeatureDefinition.Instantiate(web, new Guid(typeGuid));
                Tracker = ListFeatureTracker.Instantiate(this);

                return true;
            }
        }

        protected void SetGuid(Guid featureGuid)
        {
            Id = featureGuid;
            KeyManager = new ListFeatureKeyManager(featureGuid, ParentList);
        }

        public void Register(object target)
        {
            WrapperObject wrapper = new WrapperObject(target);
             
            List<ListFeatureSetting> settings = Settings.Get();
            foreach (ListFeatureSetting setting in settings)
            {
                wrapper.SetProperty(setting.Name, setting.Value);
            }
        }
        #endregion // Instantiating

        #region Core Addition
        protected internal bool Create(SPList list, ListFeatureDefinition definition, InitListFeature initListFeature)
        {
            ListFeatureEventReceiverProperties properties;
            definition.TriggerEvent(ListFeatureEventReceiverType.FeatureAdding, definition, list, out properties);

            if (!properties.Cancel)
            {
                ParentList = list;
                SetGuid(Guid.NewGuid());

                if (definition != null)
                {
                    Definition = definition;
                    SetPropertyValue("Definition", Definition.Id.ToString());
                    Tracker = ListFeatureTracker.Instantiate(this);

                    // Initialize the list feature
                    if (initListFeature != null)
                        initListFeature(this);


                    // Triggers the added event
                    Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureAdded, this);
                    return true;
                }
            }
            return false;
       }

        public void Delete()
        {
            ListFeatureEventReceiverProperties properties;
            Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureRemoving, this, out properties);

            if (!properties.Cancel)
            {
                Deactivate();
                DeleteAllPropertyValue(KeyManager);
            }

            Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureRemoved, this);
        }

        #endregion // Core Addition

        #region Core Activation
        public void Activate()
        {
            if (!IsActive)
            {
                ListFeatureEventReceiverProperties properties;
                Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureActivating, this, out properties);

                if (!properties.Cancel)
                {
                    Elements.Provision(this);
                    SetPropertyValue("Activated", true.ToString());
                    Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureActivated, this);
                }
            }

        }
        public void Deactivate()
        {
            if (IsActive)
            {
                ListFeatureEventReceiverProperties properties;
                Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureDeactivating, this, out properties);
                if (!properties.Cancel)
                {
                    Elements.Unprovision(this);
                    SetPropertyValue("Activated", false.ToString());
                    Definition.TriggerEvent(ListFeatureEventReceiverType.FeatureDeactivated, this);
                }
            }
        }

        public bool IsActive
        {
            get
            {
                if (Definition.Exists)
                {
                    string activated = GetPropertyValue("Activated");

                    if (!string.IsNullOrEmpty(activated))
                    {
                        return Helper.GetBooleanOrDefault(activated, false);
                    }
                }
                return false;
            }
        }

        public bool IsInstalled
        {
            get
            {
                return KeyManager != null && Id != null && Id != Guid.Empty;
            }
        }
        #endregion // Activation

        #region Static Core
        public static ListFeature Current
        {
            get
            {
                return Instantiate(
                    new Guid(HttpContext.Current.Request["Feature"].ToString()),
                    SPContext.Current.List);
            }
        }

        public static ListFeature Instantiate(Guid guid, SPList list)
        {
            ListFeature listFeature = new ListFeature();

            if (listFeature.Instantiate(list, guid))
                return listFeature;
            else
                return null;
        }

        public static ListFeature Instantiate(Guid guid, SPList list, SPWeb web)
        {
            ListFeature listFeature = new ListFeature();

            if (listFeature.Instantiate(list, guid, web))
                return listFeature;
            else
                return null;
        }

        public static ListFeature Reinstantiate(ListFeature listFeature, SPWeb web)
        {
            if(listFeature != null)
                return ListFeature.Instantiate(listFeature.Id, web.Lists[listFeature.ParentList.ID], web);
            else
                return null;
        }

        public static List<ListFeature> GetListFeatures(SPList list)
        {
            List<ListFeature> newList = new List<ListFeature>();

            if (list != null)
            {
                SPWeb web = list.ParentWeb;
                try
                {
                    List<string> guidStrings = new List<string>();
                    List<string> properties = GetProperties(new ListFeatureKeyManager(list), web);

                    properties.ForEach(delegate(string key)
                    {
                        string guid = key.Substring(0, 36);

                        if (!guidStrings.Contains(guid))
                            guidStrings.Add(guid);
                    });

                    guidStrings.ForEach(delegate(string guid)
                    {
                        ListFeature listFeature = ListFeature.Instantiate(new Guid(guid), list, web);
                        if (listFeature != null)
                            newList.Add(listFeature);
                    });
                }
                finally
                {
                    // SPHelper.TryDisposeWeb(web);
                }
            }
            return newList;
        }

        public static List<ListFeature> GetListFeatures(SPList list, ListFeatureDefinition definition)
        {
            List<ListFeature> newList = new List<ListFeature>();

            if (list != null)
            {
                SPWeb web = list.ParentWeb;
                try
                {
                    List<string> guidStrings = new List<string>();
                    List<string> properties = GetProperties(new ListFeatureKeyManager(list), web);

                    properties.ForEach(delegate(string key)
                    {
                        string guid = key.Substring(0, 36);

                        if (!guidStrings.Contains(guid))
                            guidStrings.Add(guid);
                    });

                    guidStrings.ForEach(delegate(string guid)
                    {
                        ListFeature listFeature = ListFeature.Instantiate(new Guid(guid), list, web);
                        if (listFeature != null && listFeature.Definition.Id == definition.Id)
                            newList.Add(listFeature);
                    });
                }
                finally
                {
                    // SPHelper.TryDisposeWeb(web);
                }
            }
            return newList;
        }

        public static List<ListFeature> GetActiveListFeatures(SPList list)
        {
            List<ListFeature> lstFeats = GetListFeatures(list);
            List<ListFeature> newList = new List<ListFeature>();

            lstFeats.ForEach(delegate(ListFeature listFeature)
            {
                if (listFeature.IsActive)
                    newList.Add(listFeature);
            });
            return newList;
        }

        public static List<ListFeature> GetActiveListFeatures(SPList list, ListFeatureDefinition definition)
        {
            List<ListFeature> lstFeats = GetListFeatures(list, definition);
            List<ListFeature> newList = new List<ListFeature>();

            lstFeats.ForEach(delegate(ListFeature listFeature)
            {
                if (listFeature.IsActive)
                    newList.Add(listFeature);
            });
            return newList;
        }

        public static List<ListFeature> GetInactiveListFeatures(SPList list)
        {
            List<ListFeature> lstFeats = GetListFeatures(list);
            List<ListFeature> newList = new List<ListFeature>();

            lstFeats.ForEach(delegate(ListFeature listFeature)
            {
                if (!listFeature.IsActive)
                    newList.Add(listFeature);
            });
            return newList;
        }
        public static List<ListFeature> GetInactiveListFeatures(SPList list, ListFeatureDefinition definition)
        {
            List<ListFeature> lstFeats = GetListFeatures(list, definition);
            List<ListFeature> newList = new List<ListFeature>();

            lstFeats.ForEach(delegate(ListFeature listFeature)
            {
                if (! listFeature.IsActive)
                    newList.Add(listFeature);
            });
            return newList;
        }
        #endregion // Static Core

        #region Core Property Management
        public void SetPropertyValue(string key, string value)
        {
            SPWeb web = ParentList.ParentWeb;

            try
            {
                SetPropertyValue(KeyManager.GenerateKey(key), value, web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }
        }
        public string GetPropertyValue(string key)
        {
            string result = null;

            SPWeb web = ParentList.ParentWeb;

            try
            {
                result = GetPropertyValue(KeyManager.GenerateKey(key), web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }

            return result;
        }
        public void DeletePropertyValue(string key)
        {
            SPWeb web = ParentList.ParentWeb;

            try
            {
                DeletePropertyValue(KeyManager.GenerateKey(key), web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }
        }

        protected internal void SetPropertyValue(KeyManager keyManager, string key, string value)
        {
            SPWeb web = ParentList.ParentWeb;

            try
            {
                SetPropertyValue(keyManager.GenerateKey(key), value, web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }
        }
        protected internal string GetPropertyValue(KeyManager keyManager, string key)
        {
            string result = null;

            SPWeb web = ParentList.ParentWeb;

            try
            {
                result = GetPropertyValue(keyManager.GenerateKey(key), web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }

            return result;
        }
        protected internal void DeletePropertyValue(KeyManager keyManager, string key)
        {
            SPWeb web = ParentList.ParentWeb;

            try
            {
                DeletePropertyValue(keyManager.GenerateKey(key), web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }
        }
        protected internal void DeleteAllPropertyValue(KeyManager keyManager)
        {
            SPWeb web = ParentList.ParentWeb;

            try
            {
                DeleteProperties(keyManager, web);
            }
            finally
            {
                // SPHelper.TryDisposeWeb(web);
            }
        }

        ///////////////STATIC//////////////////////////
        protected internal static void Invalidate(SPWeb web)
        {
            WrapperObject o = new WrapperObject(web);
            o.SetField("m_htProperties", null);
            o.SetField("m_PropertyArray", null);
        }

        protected internal static void SetPropertyValue(string key, string value, SPWeb web)
        {
            Invalidate(web);
            web.AllProperties[key] = value;
            Update(web);
        }

        protected internal static string GetPropertyValue(string key, SPWeb web)
        {
            object value = web.AllProperties[key];

            if (value == null)
                return null;
            else
                return value.ToString();
        }

        protected internal static void DeletePropertyValue(string key, SPWeb web)
        {
            DeletePropertyValue(key, web, true);
        }

        protected internal static void DeletePropertyValue(string key, SPWeb web, bool immediateUpdate)
        {
            Invalidate(web);
            web.AllProperties.Remove(key);

            if (immediateUpdate)
                Update(web);
        }

        protected internal static List<string> GetProperties(KeyManager manager, SPWeb web)
        {
            List<string> properties = new List<string>();

            foreach (DictionaryEntry entry in web.AllProperties)
            {
                string key = entry.Key.ToString();
                if (manager.IsValidKey(key))
                    properties.Add(manager.ExtractInternalName(key));
            }
            return properties;
        }

        protected internal static void DeleteProperties(KeyManager manager, SPWeb web)
        {
            List<string> properties = new List<string>();

            Invalidate(web);
            foreach (DictionaryEntry entry in web.AllProperties)
            {
                string key = entry.Key.ToString();
                if (manager.IsValidKey(key))
                    properties.Add(key);
            }

            foreach (string property in properties)
            {
                web.AllProperties.Remove(property);
            }

            Update(web);
        }

        protected internal static void Update(SPWeb web)
        {
            web.AllowUnsafeUpdates = true;
            web.Update();
            web.AllowUnsafeUpdates = false;
        }
        #endregion // Core Property Management


        public string GetFromCollection(object obj, NameValueCollection collection, RetrievalMethod retrievalMethod)
        {
            return collection[ConvertKey(obj, retrievalMethod)];
        }

        /// <summary>
        /// This method will convert any metadata to a key relevant to the retrieval method. 
        /// </summary>
        /// <param name="obj">The key to convert</param>
        /// <param name="retrievalMethod">Choose between a unique key or an identity key.</param>
        /// <returns></returns>
        public string ConvertKey(object obj, RetrievalMethod retrievalMethod)
        {
            KeyManager keyManager = null;

            if (retrievalMethod == RetrievalMethod.Unique)
                keyManager = KeyManager;
            else if (retrievalMethod == RetrievalMethod.Identity)
                keyManager = Definition.KeyManager;

            return keyManager.GenerateKey(obj.ToString());
        }
    }
}
